(* Lexer for configfile *)

{
open Parser	(* The type token is defined in parser.mli *)

exception Invalid_token of string

let raise_parser_error lexbuf str =
  Printf.printf "error: lexing %s: invalid token %s\n"
    (Configutil.string_of_pos lexbuf.Lexing.lex_curr_p) str;
  raise (Invalid_token str)

}

(* Rule for whitespace as it's used so often *)
let whitespace    = [' ' '\t']

(* Separators, option <separator> value *)
let separator     = [  ':' '=']

(* Newlines for different platforms *)
let newline       = ('\n' | "\r\n")

(* Comments begin with # and go to end of line *)
let comment       = ['#' ';'] ( [^ '\n']* )

(* How do we define a section begin and contents *)
let sect_begin    = '['
let sect_end      = ']'

(* Section, option names *)
let section_name  = [^ ']' '\n' '\r' ' ' '\t' ':' '=' '#' ';']
let option_name   = [^ ']' '\n' '\r' ' ' '\t' ':' '=' '#' ';']

(* Option value *)
let option_value  = [^ '\n' '\r' '#' ';' '\\' ]

(* An option line is of the form optname=optvalue

   - User may place leading or trailing spaces around the name,
   or before the value.
   - Ex Line: optname = value

   - User may use trailing \ to quote the end of line.
   - Ex Line: optname = value\
   - Ex Line: rest of value

   - User may use double quotes for quoting leading and trailing
   whitespace. However, the line must end with a trailing double
   quote. Anything after that quote is considered junk.
   - Ex Line: optname = " value"
   - Ex Line: optname = "value  "

   - TBD: User may refer to other values by using %(ref)s syntax. The
   "ref" here may be a string, which will reference values in the
   current section, or at the top level.
   - Ex Line: opt=value
   - Ex Line: anotheropt=%(opt)s
*)
let option_line =
  (option_name+ as optname)
    whitespace*
    separator
    whitespace*


rule token = parse
  | whitespace
      { token lexbuf }    (* skip blanks *)
  | newline
      { Configutil.incr_linenum lexbuf; token lexbuf }
  | comment
      { token lexbuf }    (* skip comments *)
  | sect_begin (section_name+ as id) sect_end?
      { SECTION(id) }
  | option_line
      { optvalue optname (Buffer.create 80) lexbuf }
  | eof
      { EOF }
  |  _ as t
      { raise_parser_error lexbuf (Char.escaped t) }
and optvalue opt b = parse
  | '\\' ('\n' | "\r\n")
      { Configutil.incr_linenum lexbuf; optvalue opt b lexbuf }
  | '\\'
      { Buffer.add_char b '\\'; optvalue opt b lexbuf }
  | option_value+ as v
      { Buffer.add_string b v; optvalue opt b lexbuf }
  | comment
      { OPTION (opt, Buffer.contents b) }
  | newline
      { Configutil.incr_linenum lexbuf
      ; OPTION (opt, Buffer.contents b)
      }
  | eof
      { Printf.eprintf "error: EOF in middle of escaped line\n"
      ; EOF
      }
